package com.my.processor.test.one;

import com.google.auto.service.AutoService;
import com.my.processor.common.AbstractLogProcessor;
import com.sun.tools.javac.model.JavacElements;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.List;

import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import java.util.LinkedHashSet;
import java.util.Set;

/**
 *  其中process为主要方法，在里面处理接收到的所有被PrintMe修饰过的元素，这里是直接输出器信息。
 * #https://www.cnblogs.com/avenwu/p/4173899.html
 * #https://blog.csdn.net/jinlong59421/article/details/106463565
 * #https://blog.csdn.net/ucxiii/article/details/52025005?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.control
 * #问题处理
 * #https://blog.csdn.net/zimou5581/article/details/98208143
 *
 * 修改代码 添加打印输出  列斯lombok 的data注解 添加set get方法
 *
 * @author timo 2021/2/18
 */
@AutoService(Processor.class)
public class MyProcessor extends AbstractLogProcessor {

    @Override
    protected boolean processImpl(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        for (TypeElement te : annotations) {
            for (Element e : roundEnv.getElementsAnnotatedWith(te)) {
                log( "MyProcessor Printing " + e.toString());
            }
        }
        //为加了这个注解的方法添加一句打印语句  这种代码没有办法debug
        //https://blog.csdn.net/dap769815768/article/details/90448451
        final Context context = ((JavacProcessingEnvironment) processingEnv).getContext();
        final JavacElements elementUtils = (JavacElements) processingEnv.getElementUtils();
        final TreeMaker treeMaker = TreeMaker.instance(context);
        Set<? extends Element> elements = roundEnv.getRootElements();

        for (Element element : roundEnv.getElementsAnnotatedWith(PrintMe.class)) {
            //这里转化为方法，所以属性上不能加这个注解了
            JCTree.JCMethodDecl jcMethodDecl = (JCTree.JCMethodDecl) elementUtils.getTree(element);
            //treeMaker.Select（生成具体的方法），一个是treeMaker.Literal（方法的参数）
            treeMaker.pos = jcMethodDecl.pos;
            jcMethodDecl.body = treeMaker.Block(0, List.of(
                    treeMaker.Exec(
                            treeMaker.Apply(
                                    List.<JCTree.JCExpression>nil(),
                                    treeMaker.Select(
                                            treeMaker.Select(
                                                    treeMaker.Ident(
                                                            elementUtils.getName("System")
                                                    ),
                                                    elementUtils.getName("out")
                                            ),
                                            elementUtils.getName("println")
                                    ),
                                    List.<JCTree.JCExpression>of(
                                            treeMaker.Literal("Hello, world!!!")
                                    )
                            )
                    ),
                    jcMethodDecl.body,
                    //这里之所以最终生成的两句完全一样，是因为编译器编译之后会把java.lang给省略掉，这个是编译器优化所致。
                    treeMaker.Exec(
                            treeMaker.Apply(
                                    List.<JCTree.JCExpression>nil(),
                                    treeMaker.Select(
                                            treeMaker.Select(
                                                    treeMaker.Select(
                                                            treeMaker.Select(
                                                                    treeMaker.Ident(elementUtils.getName("java")),
                                                                    elementUtils.getName("lang")
                                                            ),
                                                            elementUtils.getName("System")
                                                    ),
                                                    elementUtils.getName("out")
                                            ),
                                            elementUtils.getName("println")
                                    ),
                                    List.<JCTree.JCExpression>of(
                                            treeMaker.Literal("Hello, world!!!")
                                    )
                            )
                    )
            ));
        }
        // process方法最终返回的是false，这个返回值涉及到编译器的处理逻辑，返回true表示已经处理，返回false表示未被处理，也就是说后续还会有操作。
        // 这里用true合理，还是false合理，由于我目前对于便编译器的编译逻辑还不清楚，所以目前还没法给出确切的答案，当然，在此例子中，true和false看起来都没什么问题。
        return true;
    }

    /**
     * 指定注解类
     * @return
     */
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> annotations = new LinkedHashSet<>();
        annotations.add(PrintMe.class.getCanonicalName());
        return annotations;
    }

    /**
     * 指定java版本
     * @return
     */
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }
}
